/*
 * report_creator.cpp
 *
 *  Created on: 28.07.2013
 *      Author: SpirTanol
 */

#include "report_creator.h"
#include "device/device_info.h"
#include "packets/watch_data_packet.h"
#include "packets/config_packet.h"
#include "events/event_protocol_event.h"
#include "events/crash_protocol_event.h"
#include "events/app_protocol_event.h"
#include "ui/watch_plot.h"
#include "ui/temp_plot.h"
#include "ui/run_config_plot.h"
#include "ui/crash_plot.h"

// ModbusLib
#include "modbus.h"

// Qt
#include <QPixmap>
#include <QDateTime>
#include <QPrinter>
#include <QPainter>
#include <QFont>
#include <QLocale>
#include <QDebug>

//=================================================================================================
ReportCreator::ReportCreator() : oDevice(0), oWatchData(0), oConfigData(0), oEventsPtr(0), oWatchPlot(0), oTempPlot(0), oRunConfigPlot(0),
	oCrashPlot(0)
{

}

ReportCreator::~ReportCreator()
{

}
//=================================================================================================
bool ReportCreator::create(const QString &fileName, const DeviceInfo *device, const WatchDataPacket *watchData, const ConfigPacket *configData,
		const EventList *events, const WatchPlot *watchPlot, const TempPlot *tempPlot, const RunConfigPlot* runConfigPlot, const CrashPlot *crashPlot,
		uint crashPlotWriteTime, int crashPlotCode)
{
	extern int gDeviceVersion;

	oFileName = fileName;
	oDevice = device->clone();
	oWatchData = watchData->clone();
	oConfigData = configData->clone();
	oEventsPtr = events;

	oWatchPlot = new WatchPlot(watchPlot);
	QwtText text(QString::fromUtf8("Значения фазных токов и напряжений"));
	text.setFont(QFont("Arial", 12));
	oWatchPlot->setTitle(text);

	oTempPlot = new TempPlot(tempPlot);
	text.setText(QString::fromUtf8("Температура"));
	oTempPlot->setTitle(text);

	if (runConfigPlot != 0)
	{
		oRunConfigPlot = new RunConfigPlot(runConfigPlot);
		text.setText(QString::fromUtf8("Токо-временная характеристика"));
		oRunConfigPlot->setTitle(text);
		oRunConfigPlot->buildProtectionGraph(false);
	}
	else
		oRunConfigPlot = 0;

	if (crashPlot != 0)
	{
		oCrashPlot = new CrashPlot(crashPlot);
		oCrashPlotWriteTime = crashPlotWriteTime;
		oCrashPlotCode = crashPlotCode;
	}
	else
		oCrashPlot = 0;

	QLocale locale(QLocale::Russian, QLocale::RussianFederation);
	QPrinter printer;
	QPainter painter;
	QDateTime dateTime = QDateTime::currentDateTime();

	printer.setOutputFormat(QPrinter::PdfFormat);
	printer.setOutputFileName(fileName);
	printer.setDocName(QString::fromUtf8("Отчёт от ") + dateTime.toString("dd.MM.yyyy hh:mm"));

	if (!painter.begin(&printer))
		return false;

	//Логотип
	painter.drawPixmap(580, -20, 157, 30, QPixmap(":/img/logo.png"));
	QString reportDate = QString::fromUtf8("Дата формирования отчёта: ") + dateTime.toString("dd.MM.yyyy");

	QString formatStr;
	switch (oDevice->format())
	{
	case ModbusPort::_8E1:
		formatStr = "8E1";
		break;
	case ModbusPort::_8O1:
		formatStr = "8O1";
		break;
	case ModbusPort::_8N15:
		formatStr = "8N15";
		break;
	}
	QString devStr = QString::fromUtf8("УЗД-11 ") + QString::number(oDevice->curMin()) + "-" + QString::number(oDevice->curMax()) +
			" v" + QString::number(oDevice->version() * 0.01, 'F', 2) + " / " +
			locale.toString(oDevice->addr()) + " / " + locale.toString(oDevice->speed()) +
			" / " + formatStr + " / COM" + locale.toString(oDevice->port());

	painter.setFont(QFont("Arial", 14));
	painter.drawText(0, 0, devStr);

	static const int configBeginRow = 40;
	static const int rowHeight = 12;
	int offsetCol1 = 0;
	int offsetCol2 = 300;
	int row = 0;

	// Конфиги
	painter.setFont(QFont("Arial", 12));
	painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Конфигурация УЗД"));
	painter.setFont(QFont());

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Номинальный ток двигателя:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->curNom(), 'f', (oDevice->curScale() > 1) ? 1 : 0) +
			QString::fromUtf8("А"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Минимальный ток двигателя, от номинального:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->curMin()) + "%");

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Порог защиты от перегрузки, кратность тока:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->curOverload()));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Время реакции защиты от перегрузки:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->protectTime()) + QString::fromUtf8(" мсек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Порог отключения по перекосу токов фаз:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->swOffPhaseImbalance()) + "%");

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Задержка включения:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->delayStart()) + QString::fromUtf8(" сек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Допустимое количество оборваных фаз:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->countCrashPhases()));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Время отключения при двукратной перегрузке:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->offTimeDoubleOverload()) + QString::fromUtf8(" сек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Порог срабатывания защиты по термодатчику РТ100:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->rt100temp()) + QString::fromUtf8("°С"));

	if (gDeviceVersion >= 70)
	{
		ConfigPacketV70 *config70 = static_cast<ConfigPacketV70*>(oConfigData);
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Коэффициент коррекции минимально допустимого тока:"));
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(config70->protectionThresholdOfIdle(), 'f', 1));
	}

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Быстродействующая защита "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->fastProtect() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Автоматическое повторное включение "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->autoStart() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Защита по термодатчику РТС "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->rtsProtect() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Защита по датчику влажности "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->wetnessProtect() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	offsetCol1 = 390;
	offsetCol2 = 660;
	int leftColRows = row;
	row = 0;

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Допуск на напряжение питающих фаз:"));
	QString allows[] =
	{
		QString::fromUtf8("Минус 15 + 10%"),
		QString::fromUtf8("Минус 20 + 15%"),
		QString::fromUtf8("Минус 25 + 20%"),
		QString::fromUtf8("Минус 30 + 25%"),
	};
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, allows[oConfigData->volPhaseMode()]);

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Время работы двигателя в цикл. режиме:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->workTimer()) + QString::fromUtf8(" сек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Время простоя двигателя в цикл. режиме:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->delayTimer()) + QString::fromUtf8(" сек"));

	{
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Функция входа 1:"));
		QString functions[] =
		{
			QString::fromUtf8("Нет"),
			QString::fromUtf8("Внешняя авария 1"),
			QString::fromUtf8("Пуск(Двухпроводное управление)"),
			QString::fromUtf8("Пуск(Трёхпроводное управление)"),
			QString::fromUtf8("Вращение вперёд"),
		};
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, functions[oConfigData->input1F()]);
	}

	{
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Функция входа 2:"));
		QString functions[] =
		{
			QString::fromUtf8("Нет"),
			QString::fromUtf8("Внешняя авария 2"),
			QString::fromUtf8("Пуск(Двухпроводное управление)"),
			QString::fromUtf8("Стоп(Трёхпроводное управление)"),
			QString::fromUtf8("Вращение назад"),
			QString::fromUtf8("Датчик влажности"),
		};
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, functions[oConfigData->input1F()]);
	}

	{
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Функция силового выхода:"));
		QString functions[] =
		{
			QString::fromUtf8("Стандартный"),
			QString::fromUtf8("Звезда"),
			QString::fromUtf8("Вращение вперёд"),
		};
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, functions[oConfigData->powerOutputF()]);
	}

	{
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Функция доп. выхода:"));
		QString functions[] =
		{
			QString::fromUtf8("Нет"),
			QString::fromUtf8("Сигнализация аварии"),
			QString::fromUtf8("Предупреждение о пуске"),
			QString::fromUtf8("Предаварийная сигнализация"),
			QString::fromUtf8("Дистанционно упр. выход"),
			QString::fromUtf8("Треугольник"),
			QString::fromUtf8("Вращение назад"),
			QString::fromUtf8("Управление расцепителем"),
		};
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, functions[oConfigData->appendOutputF()]);
	}

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Задержка переключения:"));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(oConfigData->switchDelay()) + QString::fromUtf8(" сек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Время срабатывания защиты от перекоса токов:"));
	if (gDeviceVersion >= 50)
	{
		ConfigPacketV50 *config50 = static_cast<ConfigPacketV50*>(oConfigData);
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::number(config50->timeProtectOfBiasCurrents()) +
				QString::fromUtf8(" мсек"));
	}
	else
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::fromUtf8("2550 мсек"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Выбор порога защиты по изоляции:"));
	if (gDeviceVersion >= 70)
	{
		ConfigPacketV70 *config70 = static_cast<ConfigPacketV70*>(oConfigData);
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++,
				((config70->protectionThresholdOfInsulation() == 1) ? QString::fromUtf8("1,1МОм") : QString::fromUtf8("0,55МОм")));
	}
	else
		painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, QString::fromUtf8("1,1МОм"));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Дистанционное управление "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->remoteControl() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Проверка последовательности фаз "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->testPhases() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Защита по термодатчику РТ100 "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->rt100Protect() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row, QString::fromUtf8("Циклическая работа по таймеру "));
	painter.drawText(offsetCol2, configBeginRow + rowHeight * row++, (oConfigData->cicleTimer() ? QString::fromUtf8("РАЗРЕШЕНО") : QString::fromUtf8("ЗАПРЕЩЕНО")));

	// Текущее состояние двигателя
	row = std::max<int>(leftColRows, row) + 1;
	offsetCol1 = 0;
	offsetCol2 = 300;

	painter.setFont(QFont("Arial", 12));
	painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Состояние УЗД"));
	painter.setFont(QFont());

	if (oWatchData->crashCode() == 0)
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Аварий нет"));
	else
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Авария: ") +
				crashText(oWatchData->crashCode()).toLower());

	painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Двигатель ") +
			(oWatchData->engineIsWoork() ? QString::fromUtf8("работает") : QString::fromUtf8("не работает")));
	{
		QString states[] =
		{
				QString::fromUtf8("обрыв"),
				QString::fromUtf8("норма"),
				QString::fromUtf8("перегрев"),
				QString::fromUtf8("замыкание"),
		};

		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Термодатчик ") + states[oWatchData->tempState()]);
	}

	{
		QString text;
		switch (oWatchData->tempRT100())
		{
		case -99:
			text = QString::fromUtf8("замыкание");
			break;
		case 999:
			text = QString::fromUtf8("обрыв");
			break;
		default:
			text = QString::fromUtf8("норма");
			break;
		}

		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Термодатчик РТ100 ") + text);
	}

	{
		QString states[] =
		{
				QString::fromUtf8("обрыв"),
				QString::fromUtf8("норма"),
				QString::fromUtf8("вода"),
		};

		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Влажность ") + states[oWatchData->wetState()]);
	}
	painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Наработка двигателя: ") +
			QString::number(oWatchData->workTime()) + QString::fromUtf8("ч."));
	if (gDeviceVersion >= 80)
		painter.drawText(offsetCol1, configBeginRow + rowHeight * row++, QString::fromUtf8("Количество пусков двигателя: ") +
					QString::number(static_cast<const WatchDataPacket80*>(oWatchData)->startCount()));

	//Логотип
	painter.drawPixmap(580, -20, 157, 30, QPixmap(":/img/logo2.png"));

	oWatchPlot->print(&painter, QRect(45, configBeginRow + rowHeight * row, 680, 300));
	oTempPlot->print(&painter, QRect(45, configBeginRow + rowHeight * row + 340, 680, 300));

	// Следующая страница отчёта
	printer.newPage();
	row = 0;
	int offsetY = 420;
	// Пусковая характеристика
	oRunConfigPlot->print(&painter, QRect(35, 0, 680, 400));

	// График аварии если есть
	if (oCrashPlot)
	{
		painter.drawText(0, offsetY + rowHeight * row++, QString::fromUtf8("Авария: ") + crashText(oCrashPlotCode) +
				"(" + QString::number(oCrashPlotCode) + ")");
		painter.drawText(0, offsetY + rowHeight * row++, QString::fromUtf8("Время окончания записи осциллограммы: ") +
				QDateTime::fromTime_t(oCrashPlotWriteTime).toString("dd.MM.yyyy hh:mm:ss"));
		oCrashPlot->print(&painter, QRect(40, offsetY + rowHeight * row++, 645, 400));

		offsetY = 820;
	}

	offsetY += 20;

	int leftCol = 0;
	int rightCol = 120;

	int pageHeight = printer.height();

	painter.drawText(540, 1060, reportDate);

	painter.setFont(QFont("Arial", 12));
	painter.drawText(0, offsetY + rowHeight * row++, QString::fromUtf8("Протокол работы УЗД"));
	painter.setFont(QFont());

	for (EventList::ConstIterator it = events->begin();
			it != events->end(); ++it)
	{
		const ProtocolEvent *e = *it;

		// Для крэша выводятся доп. данные
		int rowBuf = row;
		if (e->type() == PET_CRASH)
			rowBuf += 6;

		if (offsetY + rowHeight * rowBuf > pageHeight)
		{
			row = 0;
			offsetY = 0;
			printer.newPage();
			painter.drawText(540, 1060, reportDate);
		}

		painter.drawText(leftCol, offsetY + rowHeight * row, e->strTime());
		painter.drawText(rightCol, offsetY + rowHeight * row++, e->toString());
		if (e->type() == PET_CRASH)
		{
			const CrashProtocolEvent *ce = static_cast<const CrashProtocolEvent*>(e);

			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Ток А: ") +
					QString::number(ce->cur(P_A), 'f', (oDevice->curScale() > 1 ? 1 : 0)) + QString::fromUtf8("А"));
			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Ток B: ") +
					QString::number(ce->cur(P_B), 'f', (oDevice->curScale() > 1 ? 1 : 0)) + QString::fromUtf8("А"));
			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Ток C: ") +
					QString::number(ce->cur(P_C), 'f', (oDevice->curScale() > 1 ? 1 : 0)) + QString::fromUtf8("А"));

			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Напр. А: ") +
					QString::number(ce->vol(P_A)) + QString::fromUtf8("В"));
			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Напр. B: ") +
					QString::number(ce->vol(P_B)) + QString::fromUtf8("В"));
			painter.drawText(rightCol, offsetY + rowHeight * row++, QString::fromUtf8("Напр. C: ") +
					QString::number(ce->vol(P_C)) + QString::fromUtf8("В"));
		}

		offsetY += 5;
	}

	release();

	return painter.end();
}
//=================================================================================================
void ReportCreator::release()
{
	if (oDevice)
	{
		delete oDevice;
		delete oWatchData;
		delete oConfigData;
		delete oWatchPlot;
		delete oTempPlot;
		if (oRunConfigPlot)
		{
			delete oRunConfigPlot;
			oRunConfigPlot = 0;
		}
		if (oCrashPlot)
		{
			delete oCrashPlot;
			oCrashPlot = 0;
		}
	}
}
